package com.rede.didiok.user.modulelist.pay.service.impl.PayServiceImpl;


import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import kong.unirest.HttpResponse;
import kong.unirest.Unirest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.rede.didiok.common.constant.conf.MessageConf;
import com.rede.didiok.common.domainEvent.EventAction;
import com.rede.didiok.common.dto.pay.PayOrderDto;
import com.rede.didiok.common.enums.OrderStatusEnum;
import com.rede.didiok.common.enums.PayMethodEnum;
import com.rede.didiok.common.exception.exceptionType.InsertException;
import com.rede.didiok.common.holder.RequestHolder;
import com.rede.didiok.common.utils.ResultUtil;
import com.rede.didiok.common.utils.StringUtils;
import com.rede.didiok.common.utils.business.DomainEventUtil;
import com.rede.didiok.user.modulelist.pay.annotation.PayMethod;
import com.rede.didiok.user.modulelist.pay.dao.PayOrderDao;
import com.rede.didiok.user.modulelist.pay.entity.PayOrderEntity;
import com.rede.didiok.user.modulelist.pay.global.SysConf;
import com.rede.didiok.user.modulelist.pay.model.bean.LanTuWxPayBean;
import com.rede.didiok.user.modulelist.pay.model.dto.LantuWxPayResponseDto;
import com.rede.didiok.user.modulelist.pay.service.PayOrderService;
import com.rede.didiok.user.modulelist.pay.service.PayService;
import com.rede.didiok.user.modulelist.pay.utils.LantuPayUtil;

/**
 * 第三方微信支付服务
 *
 * @author 陌溪
 */
@Service
@PayMethod({PayMethodEnum.LANTU_WECHAT_PAY})
@Slf4j
public class LantuWechatPayService implements PayService {

    @Resource
    private PayOrderService payOrderService;

    @Resource
    private PayOrderDao payOrderDao;

    @Resource
    private LanTuWxPayBean lanTuWxPayBean;

    @Resource
    private DomainEventUtil domainEventUtil;


    // 扫码支付
    private static final String LANTU_WXPAY_NATIVE_URL = "https://api.ltzf.cn/api/wxpay/native";

    // 公众号支付便捷版
    private static final String LANTU_WXPAY_JS_API_URL = "https://api.ltzf.cn/api/wxpay/jsapi_convenient";

    //蓝兔支付成功后的回调url
    private static final String LANTU_NOTIFY_URL = "/trade/lantuWxPay/callback";

    /**
     * 普通公钥模式
     */
    private final static String NOTIFY_URL = "/trade/yunGouOSWxPay/callback";

    @Override
    public String pay(String payOrderUid) {

        // 获取支付订单
        PayOrderEntity payOrder = payOrderService.getById(payOrderUid);
        if (payOrder == null) {
            log.error("支付订单不存在: payOrderUid: {}", payOrderUid);
            throw new InsertException("支付订单不存在");
        }
        BigDecimal price = payOrder.getTotalPrice();
        String totalAmount = price.stripTrailingZeros().toPlainString();

        PayOrderDto payOrderDto = new PayOrderDto();
        BeanUtils.copyProperties(payOrder, payOrderDto);
        payOrderDto.setTimestamp(String.valueOf(System.currentTimeMillis()/1000));
        payOrderDto.setMchId(lanTuWxPayBean.getMchId());
        payOrderDto.setNotifyUrl(lanTuWxPayBean.getDomain().concat(LANTU_NOTIFY_URL));
        String sign = LantuPayUtil.createSignByOrder(payOrderDto);

        StringBuilder orderStr = new StringBuilder();
        orderStr.append("mch_id=").append(lanTuWxPayBean.getMchId())
                .append("&out_trade_no=").append(payOrderDto.getOrderNo())
                .append("&total_fee=").append(payOrderDto.getTotalPrice()) // 我这里没有退款单号，就先暂时定为订单号
                .append("&body=").append(payOrderDto.getTitle()) // 我这里没有退款单号，就先暂时定为订单号
                .append("&timestamp=").append(payOrderDto.getTimestamp())
                .append("&notify_url=").append(payOrderDto.getNotifyUrl())
                .append("&sign=").append(sign)
        ;
        log.info("蓝兔扫码支付请求数据：{}", orderStr.toString());
        HttpResponse<String> response = Unirest.post(LANTU_WXPAY_NATIVE_URL)
                .header("content-type", "application/x-www-form-urlencoded")
                .body(orderStr.toString())
                .asString();
//        String result = HttpRequest.post(LANTU_WXPAY_NATIVE_URL).form(params).execute().body();

        log.info("蓝兔扫码支付响应结果：{}", JSON.toJSONString(response));
        if(null != response && 200 == response.getStatus()){
            String body = response.getBody();
            JSONObject jsonObject = JSONObject.parseObject(body);
            Integer code = (Integer) jsonObject.get("code");
            // code  0：成功  1：失败
            if(code != null && code == 0){
                // 发送事件通知
                domainEventUtil.publishEvent(EventAction.ORDER_CREATE_SUCCESS, payOrderDto);
                LantuWxPayResponseDto responseDto = JSONObject.parseObject(jsonObject.get("data").toString(), LantuWxPayResponseDto.class);
                Map<String, String> resultMap = new HashMap<>();
                resultMap.put(SysConf.URL, responseDto == null ? null : responseDto.getCode_url());
                resultMap.put(SysConf.PAY_ORDER_UID, payOrderUid);
                return ResultUtil.successWithData(resultMap);
            }else{
                log.info("支付失败：{}",jsonObject.get("msg").toString());
            }
        }

        return ResultUtil.errorWithMessage("支付二维码生成失败！");
    }

    /**
     * JsApi 公众号支付便捷版
     *
     * @param payOrderUid 订单编码 后台生成
     * @param returnUrl 回跳地址，支付成功后用户点击【返回商户网站】跳转到该地址，跳转不会携带任何参数，如需携带参数请自行拼接。
     */
    @Override
    public String jsPay(String payOrderUid, String returnUrl) {
        // 获取支付订单
        PayOrderEntity payOrder = payOrderService.getById(payOrderUid);
        if (payOrder == null) {
            log.error("支付订单不存在: payOrderUid: {}", payOrderUid);
            throw new InsertException("支付订单不存在");
        }
        BigDecimal price = payOrder.getTotalPrice();
        String totalAmount = price.stripTrailingZeros().toPlainString();

        PayOrderDto payOrderDto = new PayOrderDto();
        BeanUtils.copyProperties(payOrder, payOrderDto);
        payOrderDto.setTimestamp(String.valueOf(System.currentTimeMillis()/1000));
        payOrderDto.setMchId(lanTuWxPayBean.getMchId());
        payOrderDto.setNotifyUrl(lanTuWxPayBean.getDomain().concat(LANTU_NOTIFY_URL));
        // 回跳地址，支付成功后用户点击【返回商户网站】跳转到该地址，跳转不会携带任何参数，如需携带参数请自行拼接。示例值：https://www.weixin.qq.com/
        payOrderDto.setReturnUrl(lanTuWxPayBean.getDomainH5().concat(returnUrl).concat(payOrderUid));
        String sign = LantuPayUtil.createSignByOrder(payOrderDto);

        StringBuilder orderStr = new StringBuilder();
        orderStr.append("mch_id=").append(lanTuWxPayBean.getMchId())
                .append("&out_trade_no=").append(payOrderDto.getOrderNo())
                .append("&total_fee=").append(payOrderDto.getTotalPrice())
                .append("&body=").append(payOrderDto.getTitle())
                .append("&timestamp=").append(payOrderDto.getTimestamp())
                .append("&notify_url=").append(payOrderDto.getNotifyUrl())
                .append("&return_url=").append(payOrderDto.getReturnUrl())
                .append("&sign=").append(sign)
        ;
        log.info("蓝兔公众号支付便捷版请求数据：{}", orderStr.toString());
        HttpResponse<String> response = Unirest.post(LANTU_WXPAY_JS_API_URL)
                .header("content-type", "application/x-www-form-urlencoded")
                .body(orderStr.toString())
                .asString();
//        String result = HttpRequest.post(LANTU_WXPAY_NATIVE_URL).form(params).execute().body();

        log.info("蓝兔公众号支付便捷版响应结果：{}", JSON.toJSONString(response));
        if(null != response && 200 == response.getStatus()){
            String body = response.getBody();
            JSONObject jsonObject = JSONObject.parseObject(body);
            Integer code = (Integer) jsonObject.get("code");
            // code  0：成功  1：失败
            if(code != null && code == 0){
                // 发送事件通知
                domainEventUtil.publishEvent(EventAction.ORDER_CREATE_SUCCESS, payOrderDto);
                LantuWxPayResponseDto responseDto = JSONObject.parseObject(jsonObject.get("data").toString(), LantuWxPayResponseDto.class);
                Map<String, String> resultMap = new HashMap<>();
                resultMap.put(SysConf.URL, responseDto == null ? null : responseDto.getOrder_url());
                resultMap.put(SysConf.PAY_ORDER_UID, payOrderUid);
                return ResultUtil.successWithData(resultMap);
            }else{
                log.info("公众号支付便捷版失败：{}",jsonObject.get("msg").toString());
            }
        }

        return ResultUtil.errorWithMessage("支付二维码生成失败！");
    }

    /**
     * 支付回调
     *
     * @return
     */
    @Override
    public String callback( HttpServletRequest request) {
        return null;
    }

    @Override
//    @Transactional(rollbackFor = Exception.class)
    public String callback() {
        HttpServletRequest request = RequestHolder.getRequest();
//        String sign2 = request.getParameter("sign");
//        log.info("接收到蓝兔支付第三方支付回调通知,sign:{}",sign2);
//        Map<String, String[]> parameterMap = request.getParameterMap();
//        String jsonStr2 = JSONObject.toJSONString(parameterMap);
//        String jsonStr2 = String.valueOf(request.getBody());
//        log.info("接收到蓝兔支付第三方支付回调通知,jsonStr2:{}",jsonStr2);
//        JSONObject obj = JSONObject.parseObject(jsonStr2);
//        log.info("接收到蓝兔支付第三方支付回调通知 request obj :{}",obj.toJSONString());


        try {
            boolean sign = LantuPayUtil.checkNotifySign(request, lanTuWxPayBean.getKey());
            if (!sign) {
                log.error("签证校验未通过");
                return "FAIL";
            }
            String orderNo = request.getParameter("out_trade_no");
            // 注意重复通知的情况，同一订单号可能收到多次通知，请注意一定先判断订单状态
            // 注意此处签名方式需与统一下单的签名类型一致
            if (StringUtils.isNotEmpty(orderNo)) {
                // 更新订单信息
                PayOrderEntity payOrder = payOrderService.getOne(Wrappers.<PayOrderEntity>lambdaQuery()
                        .eq(PayOrderEntity::getOrderNo, orderNo).last("LIMIT 1"));
                if (payOrder == null) {
                    log.error("获取订单失败, orderNo: {}", orderNo);
                    // 发送通知等
                    return "FAIL";
                }
                if (OrderStatusEnum.PaySuccess.equals(payOrder.getOrderStatus())) {
                    log.info("支付已完成, orderNo: {}", orderNo);
                    return "SUCCESS";
                }
                payOrder.setOrderStatus(OrderStatusEnum.PaySuccess);
                payOrder.setPayTime(new Date());
                payOrder.updateById();

                // 执行分账逻辑


                // 订单支付成功
                PayOrderDto dto = new PayOrderDto();
                BeanUtils.copyProperties(payOrder, dto);
                domainEventUtil.publishEvent(EventAction.ORDER_PAY_SUCCESS, dto);
                return "SUCCESS";
            }
        } catch (Exception e) {
            log.error("[callback] 蓝兔支付第三方微信支付回调异常");
            e.printStackTrace();
        }

        return "FAIL";
    }

    /**
     * 退款回调
     *
     * @return
     */
    @Override
//    @Transactional(rollbackFor = Exception.class) // 先不加事务，因为只有订单的状态变成“OrganizerCancel”，才会【return "SUCCESS";】，蓝兔那边便也不再通知我这边，后面的代码即使有报错也不管了
    public String refundCallback() {
        HttpServletRequest request = RequestHolder.getRequest();
        log.info("接收到蓝兔支付第三方退款回调通知");
        try {
            boolean sign = LantuPayUtil.checkRefundNotifySign(request, lanTuWxPayBean.getKey());
            if (!sign) {
                log.error("签证校验未通过");
                return "FAIL";
            }
            String orderNo = request.getParameter("out_trade_no");
            // 注意重复通知的情况，同一订单号可能收到多次通知，请注意一定先判断订单状态
            // 注意此处签名方式需与统一下单的签名类型一致
            if (StringUtils.isNotEmpty(orderNo)) {
                // 更新订单信息
                PayOrderEntity payOrder = payOrderService.getOne(Wrappers.<PayOrderEntity>lambdaQuery()
                        .eq(PayOrderEntity::getOrderNo, orderNo).last("LIMIT 1"));
                if (payOrder == null) {
                    log.error("获取订单失败, orderNo: {}", orderNo);
                    // 发送通知等
                    return "FAIL";
                }
                if (OrderStatusEnum.OrganizerCancel.equals(payOrder.getOrderStatus())) {
                    log.info("活动发起人已取消, orderNo: {}", orderNo);
                    return "SUCCESS";
                }
                payOrder.setOrderStatus(OrderStatusEnum.OrganizerCancel);
                payOrder.updateById();

                // 订单取消成功
                // TODO 这里是不是要注释掉，因为，取消已发布通知了
//                PayOrderDto dto = new PayOrderDto();
//                BeanUtils.copyProperties(payOrder, dto);
//                domainEventUtil.publishEvent(EventAction.ORDER_CANCEL_SUCCESS, dto);
                return "SUCCESS";
            }
        } catch (Exception e) {
            log.error("[callback] 蓝兔支付第三方微信退款回调异常");
            e.printStackTrace();
        }
        return "FAIL";
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String closePayOrder(PayOrderEntity payOrder) {
//        String result = WxPay.closeOrder(payOrder.getUid(), lanTuWxPayBean.getMchId(), yunGouOSWxPayBean.getKey());
//        log.info("[closePayOrder] result: {}", result);
        //  将支付的费用原路返回
//        payOrder.setOrderStatus(OrderStatusEnum.OverTimeCancel);
        boolean isClose = payOrderDao.closePayOrderByUid(payOrder.getUid());
//        log.info("关订订单返回的结果：{}",isClose);
        if(!isClose){
            log.error("订单状态已变更，关闭失败");
        }
        // 订单是新建状态，没有付款，就不退款了，下面的就不要了
//        String orderNo = payOrderService.refundForCancel(payOrder);
//        if(null != orderNo){
//            log.error("调用蓝兔支付的退款接口失败,失败的订单号：{}", orderNo);
//            throw new FeignException("调用蓝兔支付的退款接口失败,失败的订单号：{}", orderNo);
//        }

        return ResultUtil.successWithMessage(MessageConf.UPDATE_SUCCESS);
    }
}
